/* async.h
   Mathieu Stefani, 05 novembre 2015

  This header brings a Promise<T> class inspired by the Promises/A+
  specification for asynchronous operations
*/

#pragma once

#include <pistache/typeid.h>

#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <type_traits>
#include <typeinfo>
#include <vector>

namespace Pistache
{
    namespace Async
    {

        class Error : public std::runtime_error
        {
        public:
            explicit Error(const char* what)
                : std::runtime_error(what)
            { }
            explicit Error(const std::string& what)
                : std::runtime_error(what)
            { }
        };

        class BadType : public Error
        {
        public:
            explicit BadType(TypeId id)
                : Error("Argument type can not be used to resolve the promise "
                        " (TypeId does not match)")
                , id_(std::move(id))
            { }

            TypeId typeId() const { return id_; }

        private:
            TypeId id_;
        };

        class BadAnyCast : public std::bad_cast
        {
        public:
            const char* what() const noexcept override { return "Bad any cast"; }
            virtual ~BadAnyCast() { }
        };

        enum class State { Pending,
                           Fulfilled,
                           Rejected };

        template <typename T>
        class Promise;

        class PromiseBase
        {
        public:
            virtual ~PromiseBase() { }
            virtual bool isPending() const   = 0;
            virtual bool isFulfilled() const = 0;
            virtual bool isRejected() const  = 0;

            bool isSettled() const { return isFulfilled() || isRejected(); }
        };

        namespace detail
        {
            template <typename Func, typename T>
            struct IsCallable
            {

                template <typename U>
                static auto test(U*)
                    -> decltype(std::declval<Func>()(std::declval<U>()), std::true_type());

                template <typename U>
                static auto test(...) -> std::false_type;

                static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
            };

            template <typename Func>
            struct IsMoveCallable : public IsMoveCallable<decltype(&Func::operator())>
            { };

            template <typename R, typename Class, typename Arg>
            struct IsMoveCallable<R (Class::*)(Arg) const>
                : public std::is_rvalue_reference<Arg>
            { };

            template <typename Func, typename Arg>
            typename std::conditional<IsMoveCallable<Func>::value, Arg&&,
                                      const Arg&>::type
            tryMove(Arg& arg)
            {
                return std::move(arg);
            }

            template <typename Func>
            struct FunctionTrait : public FunctionTrait<decltype(&Func::operator())>
            { };

            template <typename R, typename Class, typename... Args>
            struct FunctionTrait<R (Class::*)(Args...) const>
            {
                typedef R ReturnType;

                static constexpr size_t ArgsCount = sizeof...(Args);
            };

            template <typename R, typename Class, typename... Args>
            struct FunctionTrait<R (Class::*)(Args...)>
            {
                typedef R ReturnType;

                static constexpr size_t ArgsCount = sizeof...(Args);
            };

            template <typename T>
            struct RemovePromise
            {
                typedef T Type;
            };

            template <typename T>
            struct RemovePromise<Promise<T>>
            {
                typedef T Type;
            };

            template <size_t N, typename... T>
            struct nth_element;

            template <typename Head, typename... Tail>
            struct nth_element<0, Head, Tail...>
            {
                typedef Head type;
            };

            template <size_t N, typename Head, typename... Tail>
            struct nth_element<N, Head, Tail...>
            {
                typedef typename nth_element<N - 1, Tail...>::type type;
            };

        } // namespace detail

        namespace Private
        {

            struct InternalRethrow
            {
                explicit InternalRethrow(std::exception_ptr _exc)
                    : exc(std::move(_exc))
                { }

                std::exception_ptr exc;
            };

            struct IgnoreException
            {
                void operator()(std::exception_ptr) const { }
            };

            struct NoExcept
            {
                void operator()(std::exception_ptr) const { std::terminate(); }
            };

            struct Throw
            {
                void operator()(std::exception_ptr exc) const
                {
                    throw InternalRethrow(std::move(exc));
                }
            };

            struct Core;

            class Request
            {
            public:
                virtual void resolve(const std::shared_ptr<Core>& core) = 0;
                virtual void reject(const std::shared_ptr<Core>& core)  = 0;
                virtual ~Request() { }
            };

            struct Core
            {
                Core(State _state, TypeId _id)
                    : allocated(false)
                    , state(_state)
                    , exc()
                    , mtx()
                    , requests()
                    , id(_id)
                { }

                bool allocated;
                std::atomic<State> state;
                std::exception_ptr exc;

                /*
   * We need this lock because a Promise might be resolved or rejected from a
   * thread A while a continuation to the same Promise (Core) might be attached
   * at the same from a thread B. If that's the case, then we need to serialize
   * operations so that we avoid a race-condition.
   *
   * Since we have a lock, we have a blocking progress guarantee but I don't
   * expect this to be a major bottleneck as I don't expect major contention on
   * the lock If it ends up being a bottlenick, try @improving it by
   * experimenting with a lock-free scheme
   */
                std::mutex mtx;
                std::vector<std::shared_ptr<Request>> requests;
                TypeId id;

                virtual void* memory() = 0;

                virtual bool isVoid() const = 0;

                template <typename T, typename... Args>
                void construct(Args&&... args)
                {
                    if (isVoid())
                        throw Error("Can not construct a void core");

                    if (id != TypeId::of<T>())
                    {
                        throw BadType(id);
                    }

                    void* mem = memory();

                    if (allocated)
                    {
                        reinterpret_cast<T*>(mem)->~T();
                        allocated = false;
                    }

                    new (mem) T(std::forward<Args>(args)...);
                    allocated = true;
                    state     = State::Fulfilled;
                }

                virtual ~Core() { }
            };

            template <typename T>
            struct CoreT : public Core
            {
                CoreT()
                    : Core(State::Pending, TypeId::of<T>())
                    , storage()
                { }

                ~CoreT()
                {
                    if (allocated)
                    {
                        reinterpret_cast<T*>(&storage)->~T();
                        allocated = false;
                    }
                }

                template <class Other>
                struct Rebind
                {
                    typedef CoreT<Other> Type;
                };

                T& value()
                {
                    if (state != State::Fulfilled)
                        throw Error("Attempted to take the value of a not fulfilled promise");

                    return *reinterpret_cast<T*>(&storage);
                }

                bool isVoid() const override { return false; }

            protected:
                void* memory() override { return &storage; }

            private:
                typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type Storage;
                Storage storage;
            };

            template <>
            struct CoreT<void> : public Core
            {
                CoreT()
                    : Core(State::Pending, TypeId::of<void>())
                { }

                bool isVoid() const override { return true; }

            protected:
                void* memory() override { return nullptr; }
            };

            template <typename T>
            struct Continuable : public Request
            {
                explicit Continuable(const std::shared_ptr<Core>& chain)
                    : resolveCount_(0)
                    , rejectCount_(0)
                    , chain_(chain)
                { }

                void resolve(const std::shared_ptr<Core>& core) override
                {
                    if (resolveCount_ >= 1)
                        return; // TODO is this the right thing?
                            // throw Error("Resolve must not be called more than once");

                    ++resolveCount_;
                    doResolve(coreCast(core));
                }

                void reject(const std::shared_ptr<Core>& core) override
                {
                    if (rejectCount_ >= 1)
                        return; // TODO is this the right thing?
                            // throw Error("Reject must not be called more than once");

                    ++rejectCount_;
                    try
                    {
                        doReject(coreCast(core));
                    }
                    catch (const InternalRethrow& e)
                    {
                        chain_->exc   = std::move(e.exc);
                        chain_->state = State::Rejected;
                        for (const auto& req : chain_->requests)
                        {
                            req->reject(chain_);
                        }
                    }
                }

                std::shared_ptr<CoreT<T>> coreCast(const std::shared_ptr<Core>& core) const
                {
                    return std::static_pointer_cast<CoreT<T>>(core);
                }

                virtual void doResolve(const std::shared_ptr<CoreT<T>>& core) = 0;
                virtual void doReject(const std::shared_ptr<CoreT<T>>& core)  = 0;

                virtual ~Continuable() { }

                size_t resolveCount_;
                size_t rejectCount_;
                std::shared_ptr<Core> chain_;
            };

            namespace impl
            {

                template <typename T, typename Resolve, typename Reject, typename U>
                struct Continuation;

                template <typename T, typename Resolve, typename Reject, typename Res,
                          typename Cls, typename... Args>
                struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const>
                    : public Continuation<T, Resolve, Reject, Res(Args...)>
                {
                    typedef Continuation<T, Resolve, Reject, Res(Args...)> Base;

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Base(chain, std::move(resolve), std::move(reject))
                    { }
                };

                template <typename T, typename Resolve, typename Reject, typename Res,
                          typename Cls, typename... Args>
                struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)>
                    : public Continuation<T, Resolve, Reject, Res(Args...)>
                {
                    typedef Continuation<T, Resolve, Reject, Res(Args...)> Base;

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Base(chain, std::move(resolve), std::move(reject))
                    { }
                };

                // General specialization
                template <typename T, typename Resolve, typename Reject, typename Res,
                          typename... Args>
                struct Continuation<T, Resolve, Reject, Res(Args...)> : public Continuable<T>
                {

                    static_assert(sizeof...(Args) == 1,
                                  "A continuation should only take one single argument");

                    typedef typename detail::nth_element<0, Args...>::type Arg;

                    static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value,
                                  "Incompatible types detected");

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<T>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    void doResolve(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        finishResolve(resolve_(detail::tryMove<Resolve>(core->value())));
                    }

                    void doReject(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        reject_(core->exc);
                        for (const auto& req : this->chain_->requests)
                        {
                            req->reject(this->chain_);
                        }
                    }

                    template <typename Ret>
                    void finishResolve(Ret&& ret) const
                    {
                        typedef typename std::decay<Ret>::type CleanRet;
                        this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
                        for (const auto& req : this->chain_->requests)
                        {
                            req->resolve(this->chain_);
                        }
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

                // Specialization for a void-Promise
                template <typename Resolve, typename Reject, typename Res, typename... Args>
                struct Continuation<void, Resolve, Reject, Res(Args...)>
                    : public Continuable<void>
                {

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<void>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    static_assert(sizeof...(Args) == 0,
                                  "Can not attach a non-void continuation to a void-Promise");

                    void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/)
                    {
                        finishResolve(resolve_());
                    }

                    void doReject(const std::shared_ptr<CoreT<void>>& core)
                    {
                        reject_(core->exc);
                        for (const auto& req : this->chain_->requests)
                        {
                            req->reject(this->chain_);
                        }
                    }

                    template <typename Ret>
                    void finishResolve(Ret&& ret) const
                    {
                        typedef typename std::remove_reference<Ret>::type CleanRet;
                        this->chain_->template construct<CleanRet>(std::forward<Ret>(ret));
                        for (const auto& req : this->chain_->requests)
                        {
                            req->resolve(this->chain_);
                        }
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

                // Specialization for a callback returning void
                template <typename T, typename Resolve, typename Reject, typename... Args>
                struct Continuation<T, Resolve, Reject, void(Args...)> : public Continuable<T>
                {

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<T>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    static_assert(sizeof...(Args) == 1,
                                  "A continuation should only take one single argument");

                    typedef typename detail::nth_element<0, Args...>::type Arg;

                    static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value,
                                  "Incompatible types detected");

                    void doResolve(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        resolve_(core->value());
                    }

                    void doReject(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        reject_(core->exc);
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

                // Specialization for a void-Promise on a callback returning void
                template <typename Resolve, typename Reject, typename... Args>
                struct Continuation<void, Resolve, Reject, void(Args...)>
                    : public Continuable<void>
                {

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<void>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    static_assert(sizeof...(Args) == 0,
                                  "Can not attach a non-void continuation to a void-Promise");

                    void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/) override
                    {
                        resolve_();
                    }

                    void doReject(const std::shared_ptr<CoreT<void>>& core) override
                    {
                        reject_(core->exc);
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

                // Specialization for a callback returning a Promise
                template <typename T, typename Resolve, typename Reject, typename U,
                          typename... Args>
                struct Continuation<T, Resolve, Reject, Promise<U>(Args...)>
                    : public Continuable<T>
                {

                    static_assert(sizeof...(Args) == 1,
                                  "A continuation should only take one single argument");

                    typedef typename detail::nth_element<0, Args...>::type Arg;

                    static_assert(std::is_same<T, Arg>::value || std::is_convertible<T, Arg>::value,
                                  "Incompatible types detected");

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<T>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    void doResolve(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        auto promise = resolve_(detail::tryMove<Resolve>(core->value()));
                        finishResolve(promise);
                    }

                    void doReject(const std::shared_ptr<CoreT<T>>& core) override
                    {
                        reject_(core->exc);
                        for (const auto& req : core->requests)
                        {
                            req->reject(core);
                        }
                    }

                    template <typename PromiseType>
                    struct Chainer
                    {
                        explicit Chainer(const std::shared_ptr<Private::Core>& core)
                            : chainCore(core)
                        { }

                        void operator()(const PromiseType& val)
                        {
                            chainCore->construct<PromiseType>(val);
                            for (const auto& req : chainCore->requests)
                            {
                                req->resolve(chainCore);
                            }
                        }

                        std::shared_ptr<Core> chainCore;
                    };

                    template <typename Promise,
                              typename Type = typename detail::RemovePromise<Promise>::Type>
                    Chainer<Type> makeChainer(const Promise&)
                    {
                        return Chainer<Type>(this->chain_);
                    }

                    template <typename P>
                    void finishResolve(P& promise)
                    {
                        auto chainer                = makeChainer(promise);
                        std::weak_ptr<Core> weakPtr = this->chain_;
                        promise.then(std::move(chainer), [weakPtr](std::exception_ptr exc) {
                            if (auto core = weakPtr.lock())
                            {
                                core->exc   = std::move(exc);
                                core->state = State::Rejected;

                                for (const auto& req : core->requests)
                                {
                                    req->reject(core);
                                }
                            }
                        });
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

                // Specialization for a void callback returning a Promise
                template <typename Resolve, typename Reject, typename U, typename... Args>
                struct Continuation<void, Resolve, Reject, Promise<U>(Args...)>
                    : public Continuable<void>
                {

                    static_assert(sizeof...(Args) == 0,
                                  "Can not attach a non-void continuation to a void-Promise");

                    Continuation(const std::shared_ptr<Core>& chain, Resolve resolve,
                                 Reject reject)
                        : Continuable<void>(chain)
                        , resolve_(resolve)
                        , reject_(reject)
                    { }

                    void doResolve(const std::shared_ptr<CoreT<void>>& /*core*/)
                    {
                        auto promise = resolve_();
                        finishResolve(promise);
                    }

                    void doReject(const std::shared_ptr<CoreT<void>>& core)
                    {
                        reject_(core->exc);
                        for (const auto& req : core->requests)
                        {
                            req->reject(core);
                        }
                    }

                    template <typename PromiseType, typename Dummy = void>
                    struct Chainer
                    {
                        explicit Chainer(const std::shared_ptr<Private::Core>& core)
                            : chainCore(core)
                        { }

                        void operator()(const PromiseType& val)
                        {
                            chainCore->construct<PromiseType>(val);
                            for (const auto& req : chainCore->requests)
                            {
                                req->resolve(chainCore);
                            }
                        }

                        std::shared_ptr<Core> chainCore;
                    };

                    template <typename Dummy>
                    struct Chainer<void, Dummy>
                    {
                        explicit Chainer(const std::shared_ptr<Private::Core>& core)
                            : chainCore(core)
                        { }

                        void operator()()
                        {
                            auto core   = this->chain_;
                            core->state = State::Fulfilled;

                            for (const auto& req : chainCore->requests)
                            {
                                req->resolve(chainCore);
                            }
                        }

                        std::shared_ptr<Core> chainCore;
                    };

                    template <typename Promise,
                              typename Type = typename detail::RemovePromise<Promise>::Type>
                    Chainer<Type> makeChainer(const Promise&)
                    {
                        return Chainer<Type>(this->chain_);
                    }

                    template <typename P>
                    void finishResolve(P& promise)
                    {
                        auto chainer = makeChainer(promise);
                        promise.then(std::move(chainer), [=](std::exception_ptr exc) {
                            auto core   = this->chain_;
                            core->exc   = std::move(exc);
                            core->state = State::Rejected;

                            for (const auto& req : core->requests)
                            {
                                req->reject(core);
                            }
                        });
                    }

                    Resolve resolve_;
                    Reject reject_;
                };

            } // namespace impl

            template <typename T, typename Resolve, typename Reject, typename Sig>
            struct Continuation : public impl::Continuation<T, Resolve, Reject,
                                                            decltype(&Sig::operator())>
            {

                typedef impl::Continuation<T, Resolve, Reject, decltype(&Sig::operator())>
                    Base;

                Continuation(const std::shared_ptr<Core>& core, Resolve resolve,
                             Reject reject)
                    : Base(core, std::move(resolve), std::move(reject))
                { }
            };

            template <typename T, typename Resolve, typename Reject, typename Res,
                      typename... Args>
            struct Continuation<T, Resolve, Reject, Res (*)(Args...)>
                : public impl::Continuation<T, Resolve, Reject, Res(Args...)>
            {
                typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;

                Continuation(const std::shared_ptr<Core>& core, Resolve resolve,
                             Reject reject)
                    : Base(core, std::move(resolve), std::move(reject))
                { }
            };

            template <typename T, typename Resolve, typename Reject, typename Res,
                      typename Cls, typename... Args>
            struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...)>
                : public impl::Continuation<T, Resolve, Reject, Res(Args...)>
            {
                typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;

                Continuation(const std::shared_ptr<Core>& core, Resolve resolve,
                             Reject reject)
                    : Base(core, std::move(resolve), std::move(reject))
                { }
            };

            template <typename T, typename Resolve, typename Reject, typename Res,
                      typename Cls, typename... Args>
            struct Continuation<T, Resolve, Reject, Res (Cls::*)(Args...) const>
                : public impl::Continuation<T, Resolve, Reject, Res(Args...)>
            {
                typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;

                Continuation(const std::shared_ptr<Core>& core, Resolve resolve,
                             Reject reject)
                    : Base(core, std::move(resolve), std::move(reject))
                { }
            };

            template <typename T, typename Resolve, typename Reject, typename Res,
                      typename... Args>
            struct Continuation<T, Resolve, Reject, std::function<Res(Args...)>>
                : public impl::Continuation<T, Resolve, Reject, Res(Args...)>
            {
                typedef impl::Continuation<T, Resolve, Reject, Res(Args...)> Base;

                Continuation(const std::shared_ptr<Core>& core, Resolve resolve,
                             Reject reject)
                    : Base(core, std::move(resolve), std::move(reject))
                { }
            };
        } // namespace Private

        class Resolver
        {
        public:
            explicit Resolver(const std::shared_ptr<Private::Core>& core)
                : core_(core)
            { }

            Resolver(const Resolver& other) = delete;
            Resolver& operator=(const Resolver& other) = delete;

            Resolver(Resolver&& other) = default;
            Resolver& operator=(Resolver&& other) = default;

            template <typename Arg>
            bool operator()(Arg&& arg) const
            {
                if (!core_)
                    return false;

                typedef typename std::remove_reference<Arg>::type Type;

                if (core_->state != State::Pending)
                    throw Error("Attempt to resolve a fulfilled promise");

                /* In a ideal world, this should be checked at compile-time rather
     * than runtime. However, since types are erased, this looks like
     * a difficult task
     */
                if (core_->isVoid())
                {
                    throw Error("Attempt to resolve a void promise with arguments");
                }

                std::unique_lock<std::mutex> guard(core_->mtx);
                core_->construct<Type>(std::forward<Arg>(arg));

                for (const auto& req : core_->requests)
                {
                    req->resolve(core_);
                }

                return true;
            }

            bool operator()() const
            {
                if (!core_)
                    return false;

                if (core_->state != State::Pending)
                    throw Error("Attempt to resolve a fulfilled promise");

                if (!core_->isVoid())
                    throw Error("Attempt ro resolve a non-void promise with no argument");

                std::unique_lock<std::mutex> guard(core_->mtx);
                core_->state = State::Fulfilled;
                for (const auto& req : core_->requests)
                {
                    req->resolve(core_);
                }

                return true;
            }

            void clear() { core_ = nullptr; }

            Resolver clone() { return Resolver(core_); }

        private:
            std::shared_ptr<Private::Core> core_;
        };

        class Rejection
        {
        public:
            explicit Rejection(const std::shared_ptr<Private::Core>& core)
                : core_(core)
            { }

            Rejection(const Rejection& other) = delete;
            Rejection& operator=(const Rejection& other) = delete;

            Rejection(Rejection&& other) = default;
            Rejection& operator=(Rejection&& other) = default;

            template <typename Exc>
            bool operator()(Exc exc) const
            {
                if (!core_)
                    return false;

                if (core_->state != State::Pending)
                    throw Error("Attempt to reject a fulfilled promise");

                std::unique_lock<std::mutex> guard(core_->mtx);
                core_->exc   = std::make_exception_ptr(exc);
                core_->state = State::Rejected;
                for (const auto& req : core_->requests)
                {
                    req->reject(core_);
                }

                return true;
            }

            void clear() { core_ = nullptr; }

            Rejection clone() { return Rejection(core_); }

        private:
            std::shared_ptr<Private::Core> core_;
        };

        template <typename T>
        class Deferred
        {
        public:
            Deferred()
                : resolver(nullptr)
                , rejection(nullptr)
            { }

            Deferred(const Deferred& other) = delete;
            Deferred& operator=(const Deferred& other) = delete;

            Deferred(Deferred&& other) = default;
            Deferred& operator=(Deferred&& other) = default;

            Deferred(Resolver _resolver, Rejection _reject)
                : resolver(std::move(_resolver))
                , rejection(std::move(_reject))
            { }

            template <typename U>
            bool resolve(U&& arg)
            {
                typedef typename std::remove_reference<U>::type CleanU;

                static_assert(std::is_same<T, CleanU>::value || std::is_convertible<U, T>::value,
                              "Types mismatch");

                return resolver(std::forward<U>(arg));
            }

            template <typename... Args>
            void emplaceResolve(Args&&...) { }

            template <typename Exc>
            bool reject(Exc exc)
            {
                return rejection(std::move(exc));
            }

            void clear()
            {
                resolver.clear();
                rejection.clear();
            }

        private:
            Resolver resolver;
            Rejection rejection;
        };

        template <>
        class Deferred<void>
        {
        public:
            Deferred()
                : resolver(nullptr)
                , rejection(nullptr)
            { }

            Deferred(const Deferred& other) = delete;
            Deferred& operator=(const Deferred& other) = delete;

            Deferred(Deferred&& other) = default;
            Deferred& operator=(Deferred&& other) = default;

            Deferred(Resolver _resolver, Rejection _reject)
                : resolver(std::move(_resolver))
                , rejection(std::move(_reject))
            { }

            void resolve() { resolver(); }

            template <typename Exc>
            void reject(Exc _exc) { rejection(std::move(_exc)); }

        private:
            Resolver resolver;
            Rejection rejection;
        };

        static constexpr Private::IgnoreException IgnoreException {};
        static constexpr Private::NoExcept NoExcept {};
        static constexpr Private::Throw Throw {};

        namespace details
        {

            /*
 * Note that we could use std::result_of to SFINAE-out and dispatch to the right
 * call However, gcc 4.7 does not correctly support std::result_of for SFINAE
 * purposes, so we use a decltype SFINAE-expression instead.
 *
 * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3462.html and
 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56283 for reference
 */
            template <typename T, typename Func>
            auto callAsync(Func func, Resolver& resolver, Rejection& rejection)
                -> decltype(std::declval<Func>()(resolver, rejection), void())
            {
                func(resolver, rejection);
            }

            template <typename T, typename Func>
            auto callAsync(Func func, Resolver& resolver, Rejection& rejection)
                -> decltype(std::declval<Func>()(Deferred<T>()), void())
            {
                func(Deferred<T>(std::move(resolver), std::move(rejection)));
            }
        } // namespace details

        template <typename T>
        class Promise : public PromiseBase
        {
        public:
            template <typename U>
            friend class Promise;

            typedef Private::CoreT<T> Core;

            template <typename Func>
            explicit Promise(Func func)
                : core_(std::make_shared<Core>())
                , resolver_(core_)
                , rejection_(core_)
            {
                details::callAsync<T>(func, resolver_, rejection_);
            }

            Promise(const Promise<T>& other) = delete;
            Promise& operator=(const Promise<T>& other) = delete;

            Promise(Promise<T>&& other) = default;
            Promise& operator=(Promise<T>&& other) = default;

            virtual ~Promise() { }

            template <typename U>
            static Promise<T> resolved(U&& value)
            {
                static_assert(!std::is_void<T>::value,
                              "Can not resolve a void promise with parameters");
                static_assert(std::is_same<T, U>::value || std::is_convertible<U, T>::value,
                              "Incompatible value type");

                auto core = std::make_shared<Core>();
                core->template construct<T>(std::forward<U>(value));
                return Promise<T>(std::move(core));
            }

            static Promise<void> resolved()
            {
                static_assert(std::is_void<T>::value,
                              "Resolving a non-void promise requires parameters");

                auto core   = std::make_shared<Core>();
                core->state = State::Fulfilled;
                return Promise<T>(std::move(core));
            }

            template <typename Exc>
            static Promise<T> rejected(Exc exc)
            {
                auto core   = std::make_shared<Core>();
                core->exc   = std::make_exception_ptr(exc);
                core->state = State::Rejected;
                return Promise<T>(std::move(core));
            }

            bool isPending() const override { return core_->state == State::Pending; }
            bool isFulfilled() const override { return core_->state == State::Fulfilled; }
            bool isRejected() const override { return core_->state == State::Rejected; }

            template <typename ResolveFunc, typename RejectFunc>
            auto then(ResolveFunc resolveFunc, RejectFunc rejectFunc)
                -> Promise<typename detail::RemovePromise<
                    typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type>
            {

                typedef typename detail::RemovePromise<
                    typename detail::FunctionTrait<ResolveFunc>::ReturnType>::Type RetType;

                Promise<RetType> promise;

                typedef Private::Continuation<T, ResolveFunc, RejectFunc, ResolveFunc>
                    Continuation;
                std::shared_ptr<Private::Request> req = std::make_shared<Continuation>(promise.core_, resolveFunc, rejectFunc);

                std::unique_lock<std::mutex> guard(core_->mtx);
                if (isFulfilled())
                {
                    req->resolve(core_);
                }
                else if (isRejected())
                {
                    req->reject(core_);
                }

                core_->requests.push_back(req);

                return promise;
            }

        private:
            Promise()
                : core_(std::make_shared<Core>())
                , resolver_(core_)
                , rejection_(core_)
            { }

            explicit Promise(std::shared_ptr<Core>&& core)
                : core_(core)
                , resolver_(core_)
                , rejection_(core_)
            { }

            std::shared_ptr<Core> core_;
            Resolver resolver_;
            Rejection rejection_;
        };

        template <typename T>
        class Barrier
        {
        public:
            explicit Barrier(Promise<T>& promise)
                : promise_(promise)
            { }

            void wait()
            {
                if (promise_.isFulfilled() || promise_.isRejected())
                    return;

                promise_.then(
                    [&](const T&) mutable {
                        std::unique_lock<std::mutex> guard(mtx);
                        cv.notify_one();
                    },
                    [&](std::exception_ptr) mutable {
                        std::unique_lock<std::mutex> guard(mtx);
                        cv.notify_one();
                    });

                std::unique_lock<std::mutex> guard(mtx);
                cv.wait(guard,
                        [&] { return promise_.isFulfilled() || promise_.isRejected(); });
            }

            template <class Rep, class Period>
            std::cv_status wait_for(const std::chrono::duration<Rep, Period>& period)
            {
                if (promise_.isFulfilled() || promise_.isRejected())
                    return std::cv_status::no_timeout;

                promise_.then(
                    [&](const T&) mutable {
                        std::unique_lock<std::mutex> guard(mtx);
                        cv.notify_one();
                    },
                    [&](std::exception_ptr) mutable {
                        std::unique_lock<std::mutex> guard(mtx);
                        cv.notify_one();
                    });

                std::unique_lock<std::mutex> guard(mtx);
                return cv.wait_for(guard, period);
            }

        private:
            Promise<T>& promise_;
            mutable std::mutex mtx;
            std::condition_variable cv;
        };

        namespace Impl
        {
            struct Any;
        }

        class Any
        {
        public:
            friend struct Impl::Any;

            Any(const Any& other) = default;
            Any& operator=(const Any& other) = default;

            Any(Any&& other) = default;
            Any& operator=(Any&& other) = default;

            template <typename T>
            bool is() const { return core_->id == TypeId::of<T>(); }

            template <typename T>
            T cast() const
            {
                if (!is<T>())
                    throw BadAnyCast();

                auto core = std::static_pointer_cast<Private::CoreT<T>>(core_);
                return core->value();
            }

        private:
            explicit Any(const std::shared_ptr<Private::Core>& core)
                : core_(core)
            { }
            std::shared_ptr<Private::Core> core_;
        };

        namespace Impl
        {

            /* Instead of duplicating the code between whenAll and whenAny functions, the
 * main implementation is in the When class below and we configure the class
 * with a policy instead,  depending if we are executing an "all" or "any"
 * operation, how cool is that ?
 */
            struct All
            {

                struct Data
                {
                    Data(const size_t _total, Resolver _resolver, Rejection _rejection)
                        : total(_total)
                        , resolved(0)
                        , rejected(false)
                        , mtx()
                        , resolve(std::move(_resolver))
                        , reject(std::move(_rejection))
                    { }

                    const size_t total;
                    size_t resolved;
                    bool rejected;
                    std::mutex mtx;

                    Resolver resolve;
                    Rejection reject;
                };

                template <size_t Index, typename T, typename Data>
                static void resolveT(const T& val, Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    if (data->rejected)
                        return;

                    // @Check thread-safety of std::get ?
                    std::get<Index>(data->results) = val;
                    data->resolved++;

                    if (data->resolved == data->total)
                    {
                        data->resolve(data->results);
                    }
                }

                template <typename Data>
                static void resolveVoid(Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    if (data->rejected)
                        return;

                    data->resolved++;

                    if (data->resolved == data->total)
                    {
                        data->resolve(data->results);
                    }
                }

                template <typename Data>
                static void reject(std::exception_ptr exc, Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    data->rejected = true;
                    data->reject(exc);
                }
            };

            struct Any
            {

                struct Data
                {
                    Data(size_t, Resolver resolver, Rejection rejection)
                        : done(false)
                        , mtx()
                        , resolve(std::move(resolver))
                        , reject(std::move(rejection))
                    { }

                    bool done;
                    std::mutex mtx;

                    Resolver resolve;
                    Rejection reject;
                };

                template <size_t Index, typename T, typename Data>
                static void resolveT(const T& val, Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    if (data->done)
                        return;

                    // Instead of allocating a new core, ideally we could share the same core as
                    // the relevant promise but we do not have access to the promise here is so
                    // meh
                    auto core = std::make_shared<Private::CoreT<T>>();
                    core->template construct<T>(val);
                    data->resolve(Async::Any(core));

                    data->done = true;
                }

                template <typename Data>
                static void resolveVoid(Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    if (data->done)
                        return;

                    auto core = std::make_shared<Private::CoreT<void>>();
                    data->resolve(Async::Any(core));

                    data->done = true;
                }

                template <typename Data>
                static void reject(std::exception_ptr exc, Data& data)
                {
                    std::lock_guard<std::mutex> guard(data->mtx);

                    data->done = true;
                    data->reject(exc);
                }
            };

            template <typename ContinuationPolicy>
            struct When
            {
                When(Resolver resolver, Rejection rejection)
                    : resolve(std::move(resolver))
                    , reject(std::move(rejection))
                { }

                template <typename... Args>
                void operator()(Args&&... args)
                {
                    whenArgs(std::forward<Args>(args)...);
                }

            private:
                template <typename T, size_t Index, typename Data>
                struct WhenContinuation
                {
                    explicit WhenContinuation(Data _data)
                        : data(std::move(_data))
                    { }

                    void operator()(const T& val) const
                    {
                        ContinuationPolicy::template resolveT<Index>(val, data);
                    }

                    Data data;
                };

                template <size_t Index, typename Data>
                struct WhenContinuation<void, Index, Data>
                {
                    explicit WhenContinuation(Data _data)
                        : data(std::move(_data))
                    { }

                    void operator()() const { ContinuationPolicy::resolveVoid(data); }

                    Data data;
                };

                template <typename T, size_t Index, typename Data>
                WhenContinuation<T, Index, Data> makeContinuation(const Data& data)
                {
                    return WhenContinuation<T, Index, Data>(data);
                }

                template <size_t Index, typename Data, typename T>
                void when(const Data& data, Promise<T>& promise)
                {
                    promise.then(makeContinuation<T, Index>(data), [=](std::exception_ptr ptr) {
                        ContinuationPolicy::reject(std::move(ptr), data);
                    });
                }

                template <size_t Index, typename Data, typename T>
                void when(const Data& data, T&& arg)
                {
                    typedef typename std::remove_reference<T>::type CleanT;
                    auto promise = Promise<CleanT>::resolved(std::forward<T>(arg));
                    when<Index>(data, promise);
                }

                template <typename... Args>
                void whenArgs(Args&&... args)
                {
                    typedef std::tuple<typename detail::RemovePromise<
                        typename std::remove_reference<Args>::type>::Type...>
                        Results;
                    /* We need to keep the results alive until the last promise
     * finishes its execution
     */

                    /* See the trick here ? Basically, we only have access to the real type of
     * the results in this function. The policy classes do not have access to
     * the full type (std::tuple), but, instead, take a generic template data
     * type as a parameter. They only need to know that results is a tuple, they
     * do not need to know the real type of the results.
     *
     * This is some sort of compile-time template type-erasing, hue
     */
                    struct Data : public ContinuationPolicy::Data
                    {
                        Data(size_t total, Resolver resolver, Rejection rejection)
                            : ContinuationPolicy::Data(total, std::move(resolver),
                                                       std::move(rejection))
                        { }

                        Results results;
                    };

                    auto data = std::make_shared<Data>(sizeof...(Args), std::move(resolve),
                                                       std::move(reject));
                    whenArgs<0>(data, std::forward<Args>(args)...);
                }

                template <size_t Index, typename Data, typename Head, typename... Rest>
                void whenArgs(const Data& data, Head&& head, Rest&&... rest)
                {
                    when<Index>(data, std::forward<Head>(head));
                    whenArgs<Index + 1>(data, std::forward<Rest>(rest)...);
                }

                template <size_t Index, typename Data, typename Head>
                void whenArgs(const Data& data, Head&& head)
                {
                    when<Index>(data, std::forward<Head>(head));
                }

                Resolver resolve;
                Rejection reject;
            };

            template <typename T, typename Results>
            struct WhenAllRange
            {
                WhenAllRange(Resolver _resolve, Rejection _reject)
                    : resolve(std::move(_resolve))
                    , reject(std::move(_reject))
                { }

                template <typename Iterator>
                void operator()(Iterator first, Iterator last)
                {
                    auto data = std::make_shared<DataT<T>>(
                        static_cast<size_t>(std::distance(first, last)), std::move(resolve),
                        std::move(reject));

                    size_t index = 0;
                    for (auto it = first; it != last; ++it)
                    {

                        WhenContinuation<T> cont(data, index);

                        it->then(std::move(cont), [=](std::exception_ptr ptr) {
                            std::lock_guard<std::mutex> guard(data->mtx);

                            if (data->rejected)
                                return;

                            data->rejected = true;
                            data->reject(std::move(ptr));
                        });

                        ++index;
                    }
                }

            private:
                struct Data
                {
                    Data(size_t _total, Resolver _resolver, Rejection _rejection)
                        : total(_total)
                        , resolved(0)
                        , rejected(false)
                        , mtx()
                        , resolve(std::move(_resolver))
                        , reject(std::move(_rejection))
                    { }

                    const size_t total;
                    size_t resolved;
                    bool rejected;
                    std::mutex mtx;

                    Resolver resolve;
                    Rejection reject;
                };

                /* Ok so apparently I can not fully specialize a template structure
   * here, so you know what, compiler ? Take that Dummy type and leave
   * me alone
   */
                template <typename ValueType, typename Dummy = void>
                struct DataT : public Data
                {
                    DataT(size_t total, Resolver resolver, Rejection rejection)
                        : Data(total, std::move(resolver), std::move(rejection))
                    {
                        results.resize(total);
                    }

                    Results results;
                };

                /* For a vector of void promises, we do not have any results, that's
   * why we need a distinct specialization for the void case
   */
                template <typename Dummy>
                struct DataT<void, Dummy> : public Data
                {
                    DataT(size_t total, Resolver resolver, Rejection rejection)
                        : Data(total, std::move(resolver), std::move(rejection))
                    { }
                };

                template <typename ValueType, typename Dummy = void>
                struct WhenContinuation
                {
                    using D = std::shared_ptr<DataT<ValueType>>;

                    WhenContinuation(const D& _data, size_t _index)
                        : data(_data)
                        , index(_index)
                    { }

                    void operator()(const ValueType& val) const
                    {
                        std::lock_guard<std::mutex> guard(data->mtx);

                        if (data->rejected)
                            return;

                        data->results[index] = val;
                        data->resolved++;
                        if (data->resolved == data->total)
                        {
                            data->resolve(data->results);
                        }
                    }

                    D data;
                    size_t index;
                };

                template <typename Dummy>
                struct WhenContinuation<void, Dummy>
                {
                    using D = std::shared_ptr<DataT<void>>;

                    WhenContinuation(const D& _data, size_t)
                        : data(_data)
                    { }

                    void operator()() const
                    {
                        std::lock_guard<std::mutex> guard(data->mtx);

                        if (data->rejected)
                            return;

                        data->resolved++;
                        if (data->resolved == data->total)
                        {
                            data->resolve();
                        }
                    }

                    D data;
                };

                Resolver resolve;
                Rejection reject;
            };

        } // namespace Impl

        template <typename... Args,
                  typename Results = std::tuple<typename detail::RemovePromise<
                      typename std::remove_reference<Args>::type>::Type...>>
        Promise<Results> whenAll(Args&&... args)
        {
            // As ugly as it looks, this is needed to bypass a bug of gcc < 4.9
            // whereby template parameters pack inside a lambda expression are not
            // captured correctly and can not be expanded inside the lambda.
            Resolver* resolve;
            Rejection* reject;

            Promise<Results> promise([&](Resolver& resolver, Rejection& rejection) {
                resolve = &resolver;
                reject  = &rejection;
            });

            Impl::When<Impl::All> impl(std::move(*resolve), std::move(*reject));
            // So we capture everything we need inside the lambda and then call the
            // implementation and expand the parameters pack here
            impl(std::forward<Args>(args)...);

            return promise;
        }

        template <typename... Args>
        Promise<Any> whenAny(Args&&... args)
        {
            // Same trick as above;
            Resolver* resolve;
            Rejection* reject;

            Promise<Any> promise([&](Resolver& resolver, Rejection& rejection) {
                resolve = &resolver;
                reject  = &rejection;
            });

            Impl::When<Impl::Any> impl(std::move(*resolve), std::move(*reject));
            impl(std::forward<Args>(args)...);
            return promise;
        }

        template <typename Iterator,
                  typename ValueType = typename detail::RemovePromise<
                      typename std::iterator_traits<Iterator>::value_type>::Type,
                  typename Results =
                      typename std::conditional<std::is_same<void, ValueType>::value,
                                                void, std::vector<ValueType>>::type>
        Promise<Results> whenAll(Iterator first, Iterator last)
        {
            /* @Security, assert that last >= first */

            return Promise<Results>([=](Resolver& resolve, Rejection& rejection) {
                Impl::WhenAllRange<ValueType, Results> impl(std::move(resolve),
                                                            std::move(rejection));

                impl(first, last);
            });
        }

    } // namespace Async
} // namespace Pistache
